package com.ming;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;

public class MainFrame extends JFrame implements KeyListener {
    int[][] datas =new int[4][4];
    int[][] datas1 =new int[4][4];
    int loseFlag = 1;//判断失败展示失败的图片：1不展示  2展示
    int score =0; //得分
    int score1 =0; //计算撤回的得分

    int k=0;   //撤回的保存位置控制
    //撤回数组
    int[][] newArr0 = new int[4][4];
    int[][] newArr1 = new int[4][4];
    int[][] newArr2 = new int[4][4];
    int[][] newArr3 = new int[4][4];
    int[][] newArr4 = new int[4][4];
    int score01 =0; //得分的撤回保存
    int score11 =0; //得分的撤回保存
    int score21 =0; //得分的撤回保存
    int score31 =0; //得分的撤回保存
    int score41 =0; //得分的撤回保存
    Random r = new Random();


    /**
     * 此方法用于初始化数据datas
     */
    public void initData() {
        genertorNum1();
        genertorNum1();
    }

    public void genertorNum1() {
        int i = r.nextInt(4);
        int j = r.nextInt(4);
        int index03=r.nextInt(4);//  四分之一生成数字四
        if (index03!=0){
            datas[i][j] = 2;
        }else {
            datas[i][j] = 4;
        }


    }

    /**
     * 构造方法
     */
    public MainFrame() {
        initFrame();//初始化窗体
        initData();//初始化数据
        paintView();//绘制界面
        this.addKeyListener(this);//添加键盘监听
        setVisible(true); //设置窗体可见
    }

    /**
     * 用于初始化窗体，所有窗体相关设置都在在方法中完成
     */
    public void initFrame() {
        setSize(660, 570); //设置宽高
        setLocationRelativeTo(null); //设置窗体居中
        setAlwaysOnTop(true); //设置窗体置顶
        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); //设置关闭模式
        setTitle("幽冥许的Java小游戏"); //设置窗体标题
        setLayout(null); //取消默认布局
        setResizable(false);//锁定界面大小


    }

    /**
     * 绘制游戏界面
     */
    public void paintView() {

        //移除掉，界面中所有的内容
        getContentPane().removeAll();

        if (loseFlag == 2) {
            JLabel lostLabel = new JLabel(Data.over);
            lostLabel.setBounds(180, 200, 280, 135);
            getContentPane().add(lostLabel);
        }

        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                if (datas[i][j]==0){
                    JLabel image = new JLabel(Data.A0);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==2){
                    JLabel image = new JLabel(Data.A2);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==4){
                    JLabel image = new JLabel(Data.A4);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==8){
                    JLabel image = new JLabel(Data.A8);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==16){
                    JLabel image = new JLabel(Data.A16);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==32){
                    JLabel image = new JLabel(Data.A32);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==64){
                    JLabel image = new JLabel(Data.A64);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==128){
                    JLabel image = new JLabel(Data.A128);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==256){
                    JLabel image = new JLabel(Data.A256);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==512){
                    JLabel image = new JLabel(Data.A512);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==1024){
                    JLabel image = new JLabel(Data.A1024);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==2048){
                    JLabel image = new JLabel(Data.A2048);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==4096){
                    JLabel image = new JLabel(Data.A4096);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==8192){
                    JLabel image = new JLabel(Data.A8192);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==16384){
                    JLabel image = new JLabel(Data.A16384);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==32768){
                    JLabel image = new JLabel(Data.A32768);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==65536){
                    JLabel image = new JLabel(Data.A65536);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }else if (datas[i][j]==131072){
                    JLabel image = new JLabel(Data.A131072);
                    image.setBounds(120 + 100 * j, 80 + 100 * i, 95, 95);
                    getContentPane().add(image);
                }



            }
        }

        JLabel background = new JLabel(Data.BG);
        background.setBounds(108,68, 420, 420);
        getContentPane().add(background);

        JLabel scoreLabel = new JLabel("得分:" + score);
        scoreLabel.setBounds(500, 13, 1000, 25);
        scoreLabel.setFont(new Font("微软雅黑", Font.BOLD, 24));
        getContentPane().add(scoreLabel);

        JLabel withdraw = new JLabel(Data.head);
        withdraw.setBounds(0,0,660,50);
        getContentPane().add(withdraw);

        //刷新界面的方法
        getContentPane().repaint();
    }

    /**
     * 键盘被按下时，所触发的方法,主要的控制
     */
    @Override
    public void keyPressed(KeyEvent e) {
        int keyCode = e.getKeyCode();
        if (keyCode == 65) {      //A:65
            moveToLeft(1);//调用左移动方法
            genertorNum();
        } else if (keyCode == 68) { //D:68
            moveToRight(1);//调用右移动方法
            genertorNum();
        } else if (keyCode == 87) { //W:87
            moveToTop(1);//调用上移动方法
            genertorNum();
        } else if (keyCode == 83) { //S:83
            moveToBottom(1);//调用下移动方法
            genertorNum();
        } else if (keyCode==75&&score>=1000&&loseFlag==1) {
            withdraw((k - 1) % 5);//撤回
            score -= 1000;
        }else if (keyCode==32&&loseFlag==2){
            int[][] datas000 = new int[4][4];
            copyArray(datas000,datas);
            genertorNum1();
            genertorNum1();
            loseFlag=1;
            score=0;

        }else {
            return;
        }

        //每一次移动执行完，都要调用check方法，检查游戏是否是失败的状态
        check();

        paintView();//重新绘制界面
    }

    //撤回
    public void withdraw(int p) {
        k--;
        if (p==0){
            copyArray(newArr0,datas);
            score-=score11;
        }else if (p==1){
            copyArray(newArr1,datas);
            score-=score21;
        }else if (p==2){
            copyArray(newArr2,datas);
            score-=score31;
        }else if (p==3){
            copyArray(newArr3,datas);
            score-=score41;
        }else if (p==4){
            copyArray(newArr4,datas);
            score-=score01;
        }
    }

    //撤回数据的存储
    public void withdraw0(int l) {
        if (l==0){
            copyArray(datas,newArr0);
            score01=score1;
        }else if (l==1){
            copyArray(datas,newArr1);
            score11=score1;
        }else if (l==2){
            copyArray(datas,newArr2);
            score21=score1;
        }else if (l==3){
            copyArray(datas,newArr3);
            score31=score1;
        }else if (l==4){
            copyArray(datas,newArr4);
            score41=score1;
        }
        score1=0;


    }

    /**
     * 此方法用于从空白的位置，随机产生2号数字块
     */
    public void genertorNum() {
        //1.创建两个数组，准备记录二维数组中空白格子i和j的索引位置
        int[] arrayI = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
        int[] arrayJ = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};

        int w = 0;
        int e = 0;

        for (int i = 0; i < datas.length; i++) {
            for (int j = 0; j < datas[i].length; j++) {
                if (datas[i][j]!=datas1[i][j]){
                    e++;
                }
            }
        }

        //2.遍历二维数组，取出每一个元素，并判断当前元素是否是空白格式（判断是否是0）
        if (e!=0){
            for (int i = 0; i < datas.length; i++) {
                for (int j = 0; j < datas[i].length; j++) {
                    if (datas[i][j] == 0) {
                        //3.是0的话，将索引存入arrayI和arrayJ数组中
                        arrayI[w] = i;
                        arrayJ[w] = j;
                        w++;
                    }
                }
            }
        }

        //如果w变量记录不是0，代表还有空白的位置，就可以产生新的数字方块

        if (w != 0) {

            int index = r.nextInt(w);
            int x = arrayI[index];
            int y = arrayJ[index];

            int index2=r.nextInt(4);//  四分之一生成数字四
            if (index2!=0){
                datas[x][y] = 2;
            }else {
                datas[x][y] = 4;
            }

            //撤回的保存控制
            k++;
            withdraw0(k%5);
            copyArray(datas,datas1);

        }
        paintView();
    }

    /**
     * 此方法用于处理数据下移动
     */
    public void moveToBottom(int flag) {
        clockwise();//调用顺时针旋转的方法
        moveToLeft(flag);//左移动
        anticlockwise();//调用逆时针旋转的方法
    }

    /**
     * 此方法用于处理数据上移动
     */
    public void moveToTop(int flag) {
        anticlockwise();//调用逆时针旋转的方法
        moveToLeft(flag);//左移动
        clockwise();//调用顺时针旋转的方法
    }

    /**
     * 此方法用于处理右移动
     */
    public void moveToRight(int flag) {
        //1.反转数组
        hoizontalSwap();
        //2.左移动
        moveToLeft(flag);
        //3.反转数组
        hoizontalSwap();
    }

    /**
     * 此方法用于数据的左移动
     */
    public void moveToLeft(int flag) {
        for (int i = 0; i < datas.length; i++) {
            //1.后置0号元素
            int[] newArr = new int[4];
            int index = 0;
            for (int x = 0; x < datas[i].length; x++) {
                if (datas[i][x] != 0) {
                    newArr[index] = datas[i][x];
                    index++;
                }
            }

            datas[i] = newArr;

            //2.合并元素之后，后续数据前移，并在末尾补0
            for (int x = 0; x < 3; x++) {
                if (datas[i][x] == datas[i][x + 1]) {
                    datas[i][x] *= 2;
                    //计算得分
                    if (flag == 1) {
                        score1 += datas[i][x];
                        score += datas[i][x];
                    }

                    //后续数据前移，并在末尾补0
                    for (int j = x + 1; j < 3; j++) {
                        datas[i][j] = datas[i][j + 1];
                    }
                    datas[i][3] = 0;
                }
            }
        }
    }

    /**
     * 失败的状态的检查
     */
    public void check() {
        if (!checkLeft() && !checkBottom() && !checkTop() && !checkRight()) {
            //失败的状态
            loseFlag = 2;
        }
    }

    /**
     * 此方法用于二维数组的数据拷贝
     *
     * @param src  原数组
     * @param dest 目标数组
     */
    public void copyArray(int[][] src, int[][] dest) {
        for (int i = 0; i < src.length; i++) {
            for (int j = 0; j < src[i].length; j++) {
                dest[i][j] = src[i][j];
            }
        }
    }

    /**
     * 此方法判断是否可以左移动
     */
    public boolean checkLeft() {
        //1.创建新数组，用于备份原数组数据
        int[][] newArr = new int[4][4];
        //2.将原数组数据，拷贝到新数组
        copyArray(datas, newArr);
        //3.调用左移动方法，对原数组数据进行左移动
        moveToLeft(2);
        //4.使用移动后的数据，和备份的数据逐个进行比对，并使用flag变量进行记录
        //设计思路：
        //true：可以移动
        //false：不可以移动
        boolean flag = false;
        lo:
        for (int i = 0; i < datas.length; i++) {
            for (int j = 0; j < datas[i].length; j++) {
                if (datas[i][j] != newArr[i][j]) {
                    flag = true;
                    break lo;
                }
            }
        }
        //5.确定信息后，恢复原数组数据（再做一次拷贝）
        copyArray(newArr, datas);
        //6.返回结果信息
        return flag;
    }

    /**
     * 此方法判断是否可以右移动
     */
    public boolean checkRight() {
        int[][] newArr = new int[4][4];
        copyArray(datas, newArr);
        moveToRight(2);
        boolean flag = false;
        lo:
        for (int i = 0; i < datas.length; i++) {
            for (int j = 0; j < datas[i].length; j++) {
                if (datas[i][j] != newArr[i][j]) {
                    flag = true;
                    break lo;
                }
            }
        }
        copyArray(newArr, datas);
        return flag;
    }

    /**
     * 此方法判断是否可以上移动
     */
    public boolean checkTop() {
        int[][] newArr = new int[4][4];
        copyArray(datas, newArr);
        moveToTop(2);
        boolean flag = false;
        lo:
        for (int i = 0; i < datas.length; i++) {
            for (int j = 0; j < datas[i].length; j++) {
                if (datas[i][j] != newArr[i][j]) {
                    flag = true;
                    break lo;
                }
            }
        }
        copyArray(newArr, datas);
        return flag;
    }

    /**
     * 此方法判断是否可以下移动
     */
    public boolean checkBottom() {
        int[][] newArr = new int[4][4];
        copyArray(datas, newArr);
        moveToBottom(2);
        boolean flag = false;
        lo:
        for (int i = 0; i < datas.length; i++) {
            for (int j = 0; j < datas[i].length; j++) {
                if (datas[i][j] != newArr[i][j]) {
                    flag = true;
                    break lo;
                }
            }
        }
        copyArray(newArr, datas);
        return flag;
    }

    /**
     * 此方法用于处理二维数组的反转
     */
    public void hoizontalSwap() {
        for (int i = 0; i < datas.length; i++) {
            //调用reverseArray方法，对每一个一维数组进行反转
            reverseArray(datas[i]);
        }
    }

    /**
     * 此方法用于处理一维数组的反转
     */
    public void reverseArray(int[] arr) {
        for (int start = 0, end = arr.length - 1;
             start < end;
             start++, end--) {
            int temp = arr[start];
            arr[start] = arr[end];
            arr[end] = temp;
        }
    }

    /**
     * 此方法用于处理数据顺时针旋转
     */
    public void clockwise() {
        int[][] newArr = new int[4][4];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                newArr[j][3 - i] = datas[i][j];
            }
        }
        datas = newArr;
    }

    /**
     * 此方法用于处理数据逆时针旋转
     */
    public void anticlockwise() {
        int[][] newArr = new int[4][4];
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                newArr[3 - j][i] = datas[i][j];
            }
        }
        datas = newArr;
    }

    /**
     * 键盘被松开时候，所触发的方法
     */
    @Override
    public void keyReleased(KeyEvent e) {
    }

    /**
     * 无法监听到上下左右，无需关注
     */
    @Override
    public void keyTyped(KeyEvent e) {

    }

}